﻿/*************************************************
taurus Tcp协议 Socket核心类
author：Daixiwei
**************************************************/
using System;
using System.Net;
using System.Net.Sockets;
using System.Security;
using System.Threading;

namespace taurus.client
{
    class SocketTcp : ISocket
    {
        private const int READ_BUFFER_SIZE = 0x1000;
        private byte[] _byteBuffer = new byte[READ_BUFFER_SIZE];

        private Socket _connection;
        private readonly object _syncer = new object();

        public SocketTcp(SocketEngine npeer) : base(npeer)
        {
        }

        public override bool connect()
        {
            if (!base.connect())
            {
                return false;
            }
            else
            {
                state = SocketState.Connecting;
                new Thread(connectThread)
                {
                    Name = "mpnet connect thread",
                    IsBackground = true
                }.Start();
                return true;
            }
        }

        private void connectThread()
        {
            try
            {
                IPAddress ipAddress = ISocket.getIpAddress(serverAddress);
                if (ipAddress == null)
                {
                    throw new ArgumentException("Invalid IPAddress. Address: " + serverAddress);
                }
                if (ipAddress.AddressFamily != AddressFamily.InterNetwork && ipAddress.AddressFamily != AddressFamily.InterNetworkV6)
                {
                    throw new ArgumentException(string.Format("AddressFamily '{0}' not supported. Address: {1}", ipAddress.AddressFamily, serverAddress));
                }
                System.Threading.ManualResetEvent mre = new System.Threading.ManualResetEvent(false);
                var remoteEP = new IPEndPoint(ipAddress, base.serverPort);
                _connection = new Socket(ipAddress.AddressFamily, SocketType.Stream, ProtocolType.Tcp);
                _connection.SendTimeout = NetManager.WriteTimeOut * 1000;
                _connection.ReceiveTimeout = NetManager.ReadTimeOut * 1000;
                IAsyncResult result = _connection.BeginConnect(remoteEP, (res) => mre.Set(), null);
                var active = mre.WaitOne(NetManager.ConnectTimeOut*1000);
                if (active)
                {
                    _connection.EndConnect(result);
                    state = SocketState.Connected;
                    _engine.sendValidate();
                }
                else
                {
                    try
                    {
                        _connection.Close();
                    }
                    catch
                    { }
                    Logger.error("Connection error");
                    handleException(SocketCode.ExceptionOnConnect);
                    return;
                }
                
            }
            catch (SecurityException ex)
            {
                Logger.error("Connection error", ex);
                handleException(SocketCode.SecurityExceptionOnConnect);
                return;
            }
            catch (Exception ex)
            {
                Logger.error("Connection error", ex);
                handleException(SocketCode.ExceptionOnConnect);
                return;
            }
            var _thrSocketReader = new Thread(new ThreadStart(__Read));
            _thrSocketReader.IsBackground = true;
            _thrSocketReader.Start();
        }

       

        public override bool disconnect()
        {
            if (state == SocketState.Disconnected) return false;
            state = SocketState.Disconnecting;
            lock (_syncer)
            {
                if (_connection != null)
                {
                    try
                    {
                        _connection.Close();
                    }
                    catch (Exception ex)
                    {
                        Logger.error("Disconnect error", ex);
                    }
                    _connection = null;
                }
            }
            state = SocketState.Disconnected;
            return true;
        }

        public override bool send(byte[] data)
        {
            return __WriteSocket(data);
        }


        private bool __WriteSocket(byte[] buf)
        {
            if (_connection==null || state != SocketState.Connected)
            {
                Logger.error("Trying to write to disconnected socket");
                return false;
            }
            else
            {
                try
                {
                    _connection.Send(buf);
                }
                catch (Exception exception2)
                {
                    Logger.error("General error writing to socket", exception2);
                    handleException(SocketCode.SendError);
                    return false;
                }
            }
            return true;
        }


        private void __Read()
        {
            int size = 0;
            while (state == SocketState.Connected)
            {
                try
                {
                    try
                    {
                        size = _connection.Receive(_byteBuffer, 0, READ_BUFFER_SIZE, SocketFlags.None);
                    }
                    catch (SocketException ex)
                    {
                        if (state != SocketState.Disconnecting && state > SocketState.Disconnected && ex.SocketErrorCode == SocketError.WouldBlock)
                        {
                            continue; 
                        }
                        throw;
                    }
                    if (size <= 0)
                    {
                        throw new SocketException((int)SocketError.ConnectionReset);
                    }
                    if (size > _byteBuffer.Length)
                    {
                        throw new SocketException((int)SocketError.SocketError);
                    }
                    __HandleBinaryData(_byteBuffer, size);

                }catch(SocketException ex3)
                {
                    if (state != SocketState.Disconnecting && state != SocketState.Disconnected)
                    {
                        Logger.error("Receiving failed. SocketException: ", ex3);
                        if (ex3.SocketErrorCode == SocketError.ConnectionReset || ex3.SocketErrorCode == SocketError.ConnectionAborted)
                        {
                            base.handleException(SocketCode.DisconnectByServer);
                        }
                        else
                        {
                            base.handleException(SocketCode.ExceptionOnReceive);
                        }
                    }
                }
                catch (Exception ex4)
                {
                    if (state != SocketState.Disconnecting && state != SocketState.Disconnected)
                    {
                        Logger.error("Receiving Exception: ", ex4);
                        base.handleException(SocketCode.ExceptionOnReceive);
                    }
                }
            }

            this.disconnect();

        }

        private void __HandleBinaryData(byte[] buf, int size)
        {
            byte[] dst = new byte[size];
            Buffer.BlockCopy(buf, 0, dst, 0, size);
            ByteArray array = new ByteArray(dst);
            _engine._packetHandler.onDataRead(array);
        }
    }
}
